package dk.au.cs.peterbn.spoonprocessing;

import com.martiansoftware.jsap.JSAP;
import com.martiansoftware.jsap.JSAPException;
import spoon.AbstractLauncher;
import spoon.processing.Builder;
import spoon.processing.ProcessingManager;
import spoon.processing.Processor;
import spoon.processing.Severity;
import spoon.support.QueueProcessingManager;
import spoon.support.builder.CtResource;
import spoon.support.builder.FileFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: peter
 * Date: 10/12/11
 * Time: 10:33 AM
 * To change this template use File | Settings | File Templates.
 */
public class SpoonLauncher extends AbstractLauncher {

    private List<Processor<?>> processors = new ArrayList<Processor<?>>();
    private CollectingEnvironment env;
    private String cp;

    public SpoonLauncher() throws JSAPException {
        super(new String[]{});
    }

    @Override
    protected JSAP defineArgs() throws JSAPException {
        return new JSAP(); //Don't go looking for arguments
    }

    public void addInputFile(File file) {
        try {
            addInputResource(FileFactory.createResource(file));
        } catch (FileNotFoundException e) {
            getFactory().getEnvironment().report(null, Severity.ERROR,
                    "Unable to add source file : " + e.getMessage());
            if (getFactory().getEnvironment().isDebug())
                e.printStackTrace();
        }
    }

    public void addProcessor(Class<? extends Processor<?>> clazz) {
        addProcessor(clazz.getCanonicalName());
    }

    public void addProcessor(Processor<?> processor) {
        processors.add(processor);
    }

    public CollectingEnvironment getEnv() {
        return env;
    }

    @Override
    public void run() throws Exception {

        this.getFactory().getEnvironment()
                .reportProgressMessage("Spoon version 1.4");

        this.getFactory().getEnvironment().debugMessage(
                "loading command-line arguments...");
//        this.processArguments();

//        if (this.arguments.getBoolean("fragments")) {
//            this.getFactory().getEnvironment().reportProgressMessage(
//                    "running in 'fragments' mode: AST changes will be ignored");
//        }
//        this.getFactory().getEnvironment().reportProgressMessage(
//                "start processing...");

        long t = System.currentTimeMillis();
//        long tstart = t;
        this.build();
        this.getFactory().getEnvironment().debugMessage(
                "model built in " + (System.currentTimeMillis() - t) + " ms");
        t = System.currentTimeMillis();
        this.process();
        this.getFactory().getEnvironment().debugMessage(
                "model processed in " + (System.currentTimeMillis() - t)
                        + " ms");
//        t = System.currentTimeMillis();
//        this.print();
//        FileGenerator<?> fg = this.getFactory().getEnvironment()
//                .getDefaultFileGenerator();
//        if (fg != null) {
//            if (this.arguments.getBoolean("compile")) {
//                this.getFactory().getEnvironment().debugMessage(
//                        "generated bytecode in "
//                                + (System.currentTimeMillis() - t) + " ms");
//            } else {
//                this.getFactory().getEnvironment().debugMessage(
//                        "generated source in "
//                                + (System.currentTimeMillis() - t) + " ms");
//            }
//            this.getFactory().getEnvironment().debugMessage(
//                    "output directory: " + fg.getOutputDirectory());
//        }
//        t = System.currentTimeMillis();
//
//        this.getFactory().getEnvironment().debugMessage(
//                "program spooning done in " + (t - tstart) + " ms");
        this.getFactory().getEnvironment().reportEnd();

//        Gets main class
//        String progClass = this.getArguments().getString("class");
//        String progArgs[] = this.getArguments().getStringArray("arguments");

//        if (progClass != null) {
//            Launch main class using reflection
//            this.getFactory().getEnvironment().debugMessage(
//                    "running class: '" + progClass + "'...");
//            Class<?> clas = getClass().getClassLoader().loadClass(progClass);
//            Class<?> mainArgType[] = { (new String[0]).getClass() };
//            Method main = clas.getMethod("main", mainArgType);
//            Object argsArray[] = { progArgs };
//            main.invoke(null, argsArray);
//        }

    }

    @Override
    protected boolean build() {
        // building
        Builder builder = this.getFactory().getBuilder();

        try {
            for (CtResource f : this.getInputSources()) {
                builder.addInputSource(f);
            }
//            for (CtResource f : this.getTemplateSources()) {
//                builder.addTemplateSource(f);
//            }
        } catch (IOException e) {
            this.getFactory().getEnvironment().report(null, Severity.ERROR,
                    "Error while loading resource : " + e.getMessage());
            if (this.getFactory().getEnvironment().isDebug())
                e.printStackTrace();
        }
        boolean success = false;
        try {
            success = builder.build();
        } catch (Exception e) {
            this.getFactory().getEnvironment().report(null, Severity.ERROR,
                    "Error while loading resource : " + e.getMessage());
            if (this.getFactory().getEnvironment().isDebug())
                e.printStackTrace();
        }
        return success;
    }

    @Override
    protected void process() {
        // processing (consume all the processors)
        ProcessingManager processing = new QueueProcessingManager(this.getFactory());
        for (String processorName : this.getProcessorTypes()) {
            processing.addProcessor(processorName);
            this.getFactory().getEnvironment().debugMessage(
                    "Loaded processor " + processorName + ".");
        }
        for (Processor<?> processor : processors) {
            processing.addProcessor(processor);
        }

        processing.process();
    }

    @Override
    protected Factory createFactory() {
        env = new CollectingEnvironment();
        Factory factory = new Factory(this, this.env, cp);

        // environment initialization
        env.setComplianceLevel(6); //Running with compliance level 6
        env.setVerbose(true);
//        env.setXmlRootFolder(this.getArguments().getFile("properties"));

//        JavaOutputProcessor printer = new JavaOutputProcessor(this.getArguments()
//                .getFile("output"));
//        env.setDefaultFileGenerator(printer);

        env.setVerbose(true);
        env.setDebug(true);

        env.setTabulationSize(4);
        env.useTabulations(false);
        env.useSourceCodeFragments(false);
        return factory;
    }

    @Override
    protected void processArguments() {
        //Not using arguments
    }

    public void setCp(String cp) {
        this.cp = cp;
    }
}
